#ifndef TEMPLE_SIP9_MESSAGE_H_
#define TEMPLE_SIP9_MESSAGE_H_

#include "util/stdc.h"
#include "util/own.h"
#include "util/inject.h"
#include <map>
#include <list>
namespace sip {

enum Method
{
    METHOD_INVITE,
    METHOD_ACK,
    METHOD_BYE,
    METHOD_CANCEL,
    METHOD_UPDATE,
    METHOD_PRACK,
    METHOD_MESSAGE,
    METHOD_INFO,
    METHOD_REGISTER,
    METHOD_OPTIONS,
    METHOD_SUBSCRIBE,
    METHOD_NOTIFY,
    METHOD_REFER,
    METHOD_PUBLISH,
};

enum Scheme
{
    SCHEME_SIP,
    SCHEME_SIPS,
    SCHEME_TEL,
};

struct Host
{
    enum Type
    {
        HOST_NAME,
        IN4_ADDR,
        IN6_ADDR,
    };

    Type type;
    string value;

    INJECT2(Host, type, value);
};

struct UriSpec
{
    Scheme scheme;
    string user;
    string passwd;
    Host host;
    uint32 port;
    std::map<string, string> params;
    std::map<string, string> headers;

    INJECT7(UriSpec, scheme, user, passwd, host, port, params, headers);
};

struct ParamValue
{
    enum Type
    {
        TOKEN,
        QUOTED
    };

    Type type;
    string value;

    INJECT2(ParamValue, type, value);
    inline bool empty() const { return value.empty(); }
};

struct HeaderCseq
{
    uint32 seq_num;
    Method method;

    INJECT2(HeaderCseq, seq_num, method);
};

struct HeaderFrom
{
    string display;
    UriSpec uri;
    std::map<string, ParamValue> params;

    INJECT3(HeaderFrom, display, uri, params);
};

struct HeaderTo
{
    string display;
    UriSpec uri;
    std::map<string, ParamValue> params;

    INJECT3(HeaderTo, display, uri, params);
};

struct HeaderVia
{
    string proto;
    Host host;
    uint32 port;
    std::map<string, ParamValue> params;

    INJECT4(HeaderVia, proto, host, port, params);
};

struct HeaderRoute
{
    string display;
    UriSpec uri;
    std::map<string, ParamValue> params;

    INJECT3(HeaderRoute, display, uri, params);
};

struct HeaderContact
{
    enum Type
    {
        STAR,
        SPEC,
    };

    Type type;
    string display;
    UriSpec uri;
    std::map<string, ParamValue> params;

    INJECT4(HeaderContact, type, display, uri, params);
};

struct HeaderExt
{
	string name;
	string value;

    INJECT2(HeaderExt, name, value);
};

//RFC3261: The relative order of header fields with different field names is not significant.
struct Headers
{
	util::own<HeaderCseq> cseq;
	util::own<string> call_id;
	util::own<HeaderFrom> from;
	util::own<HeaderTo> to;
    std::list<HeaderVia> vias;
	std::list<HeaderRoute> routes;
	std::list<HeaderRoute> record_routes;
	std::list<HeaderContact> contacts;
    std::list<HeaderExt> exts;

    INJECT9(Headers, cseq, call_id, from, to, vias, routes, record_routes, contacts, exts);
};

struct Request
{
    Method method;
	UriSpec req_uri;
    Headers headers;
    string msg_body;

    INJECT4(Request, method, req_uri, headers, msg_body);
};

/*
1xx: Provisional -- request received, continuing to process the request;
2xx: Success -- the action was successfully received, understood, and accepted;
3xx: Redirection -- further action needs to be taken in order to complete the request;
4xx: Client Error -- the request contains bad syntax or cannot be fulfilled at this server;
5xx: Server Error -- the server failed to fulfill an apparently valid request;
6xx: Global Failure -- the request cannot be fulfilled at any server.
*/
struct Response
{
    int status_code;
	string reason;
    Headers headers;
    string msg_body;

    INJECT4(Response, status_code, reason, headers, msg_body);
};

struct Message
{
	enum Type
	{
		REQUEST,
		RESPONSE,
	};

    Type type;
    Request req;
    Response rsp;

    INJECT3(Message, type, req, rsp);

    int to_str(string &output);
    int from_str(const string &input);
    int from_str(const strref &input);
    int from_str(const char *input, int len);
};

}
#endif //TEMPLE_SIP9_MESSAGE_H_
